/***********************************************************
 * Matlab Printer
 * Programmers:
 *		Cory Frey
 *		Kendra Konopka
 *		Charles Leichner
 *		Andrew Weatherly
 *
 * Date:
 *		6 December 2010
 *
 * Description:
 *		This file implements the functions of the 
 *		MatlabPrinter.h library.
 *
 ***********************************************************/

#include "Matrix.h"
#include "MatlabPrinter.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <exception>
#include <cmath>
#include <sstream>
#include <cassert>


	/***************************************************************************/
	//               Implementation of MatlabPrinter Class                     //
	/***************************************************************************/

// Constructor (Takes a file name)
MatlabPrinter::MatlabPrinter(const char* filename){
	outputFile.open(filename);
}

MatlabPrinter::~MatlabPrinter(){
	outputFile.close();
}

std::queue<std::string> MatlabPrinter::outputQueue;

void MatlabPrinter::inputMatrix(Elements input, std::string name){
	std::stringstream output;
	output << "%Inputting matrix " << name << "." << std::endl;
	output << "disp('Inputting matrix " << name << ".')" << std::endl;
	output << name << " = [";
	for (unsigned int i = 0; i < input.at(0).size(); i++){
		for (unsigned int j = 0; j < input.size(); j++)
			output << input.at(i).at(j) << " ";
		if (i < input.at(0).size() - 1)
			output << "; ";
	}
	output << "]" << std::endl << std::endl;
	outputQueue.push(output.str());
}

// Matlab Scalar Multiply
void MatlabPrinter::scalarMultiply(double scale, Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will multiply " << rhs.getName() << " by " << scale << "." << std::endl;
	output << "disp('This will multiply " << rhs.getName() << " by " << scale << ".')" << std::endl;
	output << scale << " * " << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

// Matlab Matrix Multiply
void MatlabPrinter::matrixMultiply(Matrix lhs, Matrix rhs){
	if (lhs.getName() == "SizeBased" || rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will multiply " << lhs.getName() << " and " << rhs.getName() << "." << std::endl;
	output << "disp('This will multiply " << lhs.getName() << " and " << rhs.getName() << ".')" << std::endl;
	output << lhs.getName() << " * " << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::matrixAddition(Matrix lhs, Matrix rhs){
	if (lhs.getName() == "SizeBased" || rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will add " << lhs.getName() << " and " << rhs.getName() << "." << std::endl;
	output << "disp('This will add " << lhs.getName() << " and " << rhs.getName() << ".')" << std::endl;
	output << lhs.getName() << " + " << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::matrixSubtraction(Matrix lhs, Matrix rhs){	
	if (lhs.getName() == "SizeBased" || rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will subtract " << lhs.getName() << " and " << rhs.getName() << "." << std::endl;
	output << "disp('This will subtract " << lhs.getName() << " and " << rhs.getName() << ".')" << std::endl;
	output << lhs.getName() << " - " << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::unaryMinus(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the negative of " << rhs.getName() << "." << std::endl;
	output << "disp('This will return the negative of " << rhs.getName() << ".')" << std::endl;
	output << "-" << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::equalityCheck(Matrix lhs, Matrix rhs){
	if (lhs.getName() == "SizeBased" || rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will compare " << lhs.getName() << " and " << rhs.getName() << " and will return 1 if they are equal." << std::endl;
	output << "disp('This will compare " << lhs.getName() << " and " << rhs.getName() << " and will return 1 if they are equal.')" << std::endl;
	output << lhs.getName() << " == " << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::inequalityCheck(Matrix lhs, Matrix rhs){
	if (lhs.getName() == "SizeBased" || rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will compare " << lhs.getName() << " and " << rhs.getName() << " and will return 1 if they are not equal." << std::endl;
	output << "disp('This will compare " << lhs.getName() << " and " << rhs.getName() << " and will return 1 if they are not equal.')" << std::endl;
	output << lhs.getName() << " ~= " << rhs.getName() << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::matrixPower(Matrix lhs, double exponent){
	if (lhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will raise " << lhs.getName() << " to the " << exponent << " power." << std::endl;
	output << "disp('This will raise " << lhs.getName() << " to the " << exponent << " power.')" << std::endl;
	output << lhs.getName() << " ^ " << exponent << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::determinant(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the determinant of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the determinant of "  << rhs.getName() << ".')" << std::endl;
	output << "det( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::inverse(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the inverse of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the inverse of "  << rhs.getName() << ".')" << std::endl;
	output << "inv( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::eigenValues(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the eigen values of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the eigen values of "  << rhs.getName() << ".')" << std::endl;
	output << "eig( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::eigenVectors(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the eigen vectors of "  << rhs.getName() << " in V and the eigen values in D." << std::endl;
	output << "disp('This will return the eigen vectors of "  << rhs.getName() << " in V and the eigen values in D.')" << std::endl;
	output << "[V, D] = eig( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::echelon(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the echelon form of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the echelon form of "  << rhs.getName() << ".')" << std::endl;
	output << "rref( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::LUFactorization(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the LU factorization of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the LU factorization of "  << rhs.getName() << ".')" << std::endl;
	output << "[L, U] = lu( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::QRFactorization(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the QR factorization of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the QR factorization of "  << rhs.getName() << ".')" << std::endl;
	output << "[Q, R] = qr( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::transpose(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the transpose of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the transpose of "  << rhs.getName() << ".')" << std::endl;
	output << "transpose( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::identity(int size){
	std::stringstream output;
	output << "%This will return the identity matrix of size "  << size << "." << std::endl;
	output << "disp('This will return the identity matrix of size "  << size << ".')" << std::endl;
	output << "eye( " << size << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::trace(Matrix rhs){
	if (rhs.getName() == "SizeBased")
		return;
	std::stringstream output;
	output << "%This will return the trace of "  << rhs.getName() << "." << std::endl;
	output << "disp('This will return the trace of "  << rhs.getName() << ".')" << std::endl;
	output << "trace( " << rhs.getName() << " )" << std::endl << std::endl;
	outputQueue.push(output.str());
}

void MatlabPrinter::writeToFile(){
	outputFile << "%\tMatrix Madness" << std::endl;
	outputFile << "%\tProgrammers:" << std::endl;
	outputFile << "%\t\tCory Frey" << std::endl;
	outputFile << "%\t\tKendra Konopka" << std::endl;
	outputFile << "%\t\tCharles Leichner" << std::endl;
	outputFile << "%\t\tAndrew Weatherly" << std::endl;
	outputFile << std::endl;
	while (!outputQueue.empty()){
		outputFile << outputQueue.front();
		outputQueue.pop();
	}
	outputFile << std::endl;
}

